package org.fujene.memoryblock.local;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import org.fujene.constants.Constants;
import org.fujene.diskblock.DiskBlock;
import org.fujene.memoryblock.MemoryBlock;
import org.fujene.structure.LookupPointer;
import org.fujene.structure.SearchedTerm;
import org.fujene.toolbox.ByteConverter;

import static org.fujene.memoryblock.local.LocalDictionaryBlock.Type.*;

public class LocalDictionaryBlock extends MemoryBlock{
	private int opt_length = 0;
	// 検索種別の数
	private int opt_type = 1;

	public static enum Type{
		ALL, NEXT, DOCID, INVINDEX, STR, INVDOC,
	}

	// 1単語分の辞書
	private class DictionaryTerm{
		private int from;
		private LookupPointer nextTermPtr;
		private LookupPointer docIDPtr;
		private int strLength;
		private String str;

		private Type type;

		/** 情報読み出し用 */
		public DictionaryTerm(int from, Type type){
			this.type = type;
			this.from = from;

			if(type == ALL || type == NEXT) this.nextTermPtr = LookupPointer.fromBytes(getBody(from, PTR), 0);
			else this.nextTermPtr = new LookupPointer();
			from += PTR;
			if(type == ALL || type == DOCID) this.docIDPtr = LookupPointer.fromBytes(getBody(from, PTR), 0);
			else this.docIDPtr = new LookupPointer();
			if(type == ALL || type == STR){
				from += PTR;
				this.strLength = ByteConverter.byteToInt(getBody(from, STRLEN));
				from += STRLEN;
				try{
					this.str = new String(getBody(from, strLength), "UTF-8");
				}catch(UnsupportedEncodingException e){}
			}

		}

		/** 情報読み出し用 */
		public DictionaryTerm(int from){
			this(from, ALL);
		}

		/** 情報書き込み用 */
		public DictionaryTerm(int from, LookupPointer nextTermPtr, LookupPointer docIDPtr, String str){
			this(from, nextTermPtr, docIDPtr, str, false);
		}

		/** 情報書き込み用 */
		public DictionaryTerm(int from, LookupPointer nextTermPtr, LookupPointer docIDPtr, String str,
				boolean isWriteDocID){
			this.from = from;

			if(nextTermPtr != null) this.nextTermPtr = nextTermPtr;
			else this.nextTermPtr = LookupPointer.EMPTY;

			if(docIDPtr != null) this.docIDPtr = docIDPtr;
			else this.docIDPtr = LookupPointer.EMPTY;

			if(str != null) setString(str);
			this.type = isWriteDocID ? DOCID : ALL;
		}

		public void setString(String str){
			this.str = str;
			try{
				this.strLength = str.getBytes("UTF-8").length;
			}catch(UnsupportedEncodingException e){}
		}

		public void write(){
			if(type == ALL) bodyVacancy -= (2 * PTR) + STRLEN + strLength;
			int tmp = this.from;

			if(type != DOCID){
				setBody(this.nextTermPtr.toBytes(), tmp);
			}
			tmp += PTR;
			if(type != NEXT){
				setBody(this.docIDPtr.toBytes(), tmp);
			}
			if(type == ALL){
				tmp += PTR;
				setBody(ByteConverter.intToByte(strLength, STRLEN), tmp);
				tmp += STRLEN;
				try{
					byte[] b = str.getBytes("UTF-8");
					setBody(b, tmp);
				}catch(UnsupportedEncodingException e){}
			}
		}
	}

	public LocalDictionaryBlock(int bodySize, int ID, boolean isAllocate){
		super(bodySize, ID, Constants.MEMORY_TYPE_DICT, isAllocate);
	}

	public LookupPointer getNextPointer(int ptr){
		DictionaryTerm ptredDic = new DictionaryTerm(ptr, NEXT);

		if(ptredDic.nextTermPtr.equals(LookupPointer.EMPTY)) return null;
		else return ptredDic.nextTermPtr;
	}

	public LookupPointer getDocIDPointer(int ptr){
		DictionaryTerm ptredDic = new DictionaryTerm(ptr, DOCID);

		if(ptredDic.docIDPtr.equals(LookupPointer.EMPTY)) return null;
		else return ptredDic.docIDPtr;
	}

	// 情報読み込み
	public SearchedTerm getTerm(int ptr){
		DictionaryTerm ptredDic = new DictionaryTerm(ptr);

		if(ptredDic.nextTermPtr.equals(LookupPointer.EMPTY)) ptredDic.nextTermPtr = null;
		if(ptredDic.docIDPtr.equals(LookupPointer.EMPTY)) ptredDic.docIDPtr = null;

		return new SearchedTerm(ptredDic.str, new LookupPointer(this.getID(), ptr), ptredDic.nextTermPtr,
				ptredDic.docIDPtr);
	}

	// 情報書き込み
	public void setTerm(int ptr, LookupPointer next, LookupPointer docIDPtr, String str){
		DictionaryTerm newTerm = new DictionaryTerm(ptr, next, docIDPtr, str);

		newTerm.write();

		opt_length++;
	}

	public void setDocIDptr(int ptr, LookupPointer docIDptr){
		DictionaryTerm setTerm = new DictionaryTerm(ptr, DOCID);
		setTerm.docIDPtr = docIDptr != null ? docIDptr : LookupPointer.EMPTY;

		setTerm.write();
	}

	public void setNextPointer(int ptr, LookupPointer next){
		DictionaryTerm prevTerm = new DictionaryTerm(ptr, NEXT);
		prevTerm.nextTermPtr = next != null ? next : LookupPointer.EMPTY;

		prevTerm.write();
	}

	// サイズ確認なし
	public int getNewPointer(){
		return this.bodySize - this.bodyVacancy;
	}

	// サイズ確認つき
	public int getNewPointer(String str){
		try{
			if(this.bodyVacancy >= PTR * 2 + STRLEN + str.getBytes("UTF-8").length){
				return this.bodySize - this.bodyVacancy;
			}else{
				return -1;
			}
		}catch(UnsupportedEncodingException e){
			return -1;
		}
	}

	@Override
	public int getOptionSize(){
		return 2;
	}

	@Override
	public byte[] getOptionByteStream(){
		byte[] form = new byte[4 * 2];
		System.arraycopy(ByteConverter.intToByte(opt_length, 4), 0, form, 0, 4);
		System.arraycopy(ByteConverter.intToByte(opt_type, 4), 0, form, 4, 4);
		return form;
	}

	public int getTermCount(){
		return opt_length;
	}

	@Override
	public int getOption(int num){
		switch(num){
		case 0:
			return opt_length;
		case 1:
			return opt_type;
		default:
			return 0;
		}
	}

	@Override
	public void setOption(int option, int value){
		if(option == 0) opt_length = value;
		else if(option == 1) opt_type = value;
	}

	@Override
	public String getClassName(){
		return new String("DictionaryBlock");
	}

	@Override
	public byte[] getData() throws IOException{
		throw new NoSuchMethodError("Unimplemented method");
	}

	@Override
	public void setData(byte[] array) throws IOException{
		throw new NoSuchMethodError("Unimplemented method");
	}

	@Override
	public DiskBlock toDisk(int from, int to, String dir){
		return null;
	}
}
